/*
 * 连接发送端处理逻辑
 */

#include <stdio.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h> 
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>

#include <sm2.h>
#include <sm3.h>
#include <sm4.h>

#include "key_management.h"
#include "tools.h"
#include "common.h"

#define BUF_SIZE 1024

uint8_t *send_buf = NULL;    // 发送缓冲区
uint8_t *recv_buf = NULL;    // 接收缓冲区
uint32_t uuid;

/*
 * 获取本地蓝牙设备地址
 *
 * return 返回的是一个假地址
 */ 
uint8_t* getBluetoothAddress() {
    uint8_t *bd_addr = (uint8_t*)malloc(18);
    strcpy(bd_addr, "ff:ff:ff:ff:ff:ff");
    return bd_addr;
}

/**
 * 连接发起方初次连接函数（SM4加解密）
 */
void firstConn(int fd) {
    uint8_t iv[16] = {0};
    EC_KEY *car_key = NULL, *mobile_key = NULL;
    char hex_str[KEY_LENGTH*2+1];
    uint8_t type = FIRST_CONNECT;
    uint32_t data_size = 0, tmp = 0;

    printf("enter PIN: ");
    scanf("%s", hex_str);
    uint8_t *pin = resolveHexStr(hex_str);                              // 将输入的十六进制字符串转换成二进制码流
    printf("伪PIN码：");
    printHex(pin, KEY_LENGTH);
    uint8_t *token = getToken(pin);                                     // 使用伪PIN码生成token

    // uuid bd_addr A
    uint8_t *bd_addr = getBluetoothAddress();                           // 获取本地蓝牙地址，这里获取的是虚假地址
    uuid = getUid(pin);                                                 // 使用伪PIN码生成uid
    uint8_t *randomA = generate_secure_random_number(RANDOM_LENGTH);    // 生成密码学安全的随机数
    int bd_addr_len = strlen(bd_addr);
    int data_len = sizeof(uint32_t) + bd_addr_len + RANDOM_LENGTH;      // 计算待加密缓冲区需要大小空间
    uint8_t *data = (uint8_t*)malloc(data_len);

    // 组装要加密的数据  SM4(uuid, 蓝牙地址, randomA)
    memcpy(data, &uuid, sizeof(uint32_t));                              // 将uuid保存到待加密缓冲区
    memcpy(data + sizeof(uint32_t), bd_addr, bd_addr_len);              // 将蓝牙设备地址保存到待加密缓冲区
    memcpy(data + sizeof(uint32_t) + bd_addr_len, randomA, RANDOM_LENGTH); // 将randomA保存到待加密缓冲区
    
    printf("token: ");
    printHex(token, KEY_LENGTH);

    int enc_len = 0;
    uint8_t *enc_data = (uint8_t*)malloc(data_len + 16);
    GM_SM4_encrypt(enc_data, &enc_len, data, data_len, token, iv);      // 加密待加密数据
    
    // 协议头，按照协议格式发送，即 uuid(明文) | type(明文，标识初次连接请求或二次连接) | data_size(明文) | data（密文）
    memcpy(send_buf, &uuid, sizeof(uint32_t));                          // 将uuid保存到待发送缓冲区
    memcpy(send_buf + sizeof(uint32_t), &type, sizeof(uint8_t));        // 将type保存到待发送缓冲区
    memcpy(send_buf + sizeof(uint32_t) + sizeof(uint8_t), &enc_len, sizeof(uint32_t));  // 将data_size保存到待发送缓冲区
    memcpy(send_buf + sizeof(uint32_t)*2 + sizeof(uint8_t), enc_data, enc_len);         // 将密文保存到待发送缓冲区
    send(fd, send_buf, sizeof(uint32_t)*2 + sizeof(uint8_t) + enc_len, 0);              // 发送数据
    
    free(data);
    free(enc_data);

    int len = recv(fd, recv_buf, BUF_SIZE, 0);                                          // 等待响应数据
    memcpy(&data_size, recv_buf, sizeof(uint32_t));                                     // 从响应数据中拿到data_size（响应数据的长度）
    
    int resp_code = 0, dec_len = 0;
    uint8_t randomB[RANDOM_LENGTH], *randomC;
    uint8_t *dec_data = (uint8_t*)malloc(data_size + 16);
    uint8_t *pub_key = (uint8_t*)malloc(179);

    // 解密返回数据，同时校验解密是否成功，返回的响应码是否为SUCCESS
    GM_SM4_decrypt(dec_data, &dec_len, recv_buf + sizeof(uint32_t), data_size, token, iv);  // 解密响应数据中的密文
    memcpy(&tmp, dec_data, sizeof(uint32_t));                                           // 从解密数据中拿到uuid
    memcpy(&resp_code, dec_data + sizeof(uint32_t), sizeof(int));                       // 从解密数据中拿到响应码
    if (tmp != uuid || resp_code != SUCCESS) {                                          
        // 解密失败或响应码不是SUCCESS，结束流程
        printf("resp_code : %d\n", resp_code);
        return;
    }
    
    // 从解密数据中拿到randomB以及车载蓝牙公钥，然后生成自身的密钥对
    memcpy(randomB, dec_data + sizeof(int)*2, RANDOM_LENGTH);                           // 从接收缓冲区中拿到randomB
    memcpy(pub_key, dec_data + sizeof(int)*2 + RANDOM_LENGTH + bd_addr_len, 178);       // 从接收缓冲区拿到public_key
    pub_key[178] = 0;
    car_key = EC_KEY_new();                                                             // 创建一个新的指针用于保存车载蓝牙的公钥
    GM_SM2_key_decode(&car_key, pub_key, 0);                                            // 将车载蓝牙公钥解码到car_key，便于保存
    printf("pub_key(car): %s\n", pub_key);

    free(pub_key);
    free(dec_data);
    pub_key = NULL;
    dec_data = NULL;

    mobile_key = GM_SM2_key_new();                                                      // 生成自身的SM2密钥对
    GM_SM2_key_encode((char **)&pub_key, mobile_key, 0);                                // 将公钥编码获得BASE64编码的公钥明文，用于加密传输
    randomC = generate_secure_random_number(RANDOM_LENGTH);
    // 加密返回uid, randomC, pub_key
    data_len = strlen(pub_key) + sizeof(uint32_t) + RANDOM_LENGTH;
    data = (uint8_t*)malloc(data_len);
    enc_data = (uint8_t*)malloc(data_len + 16 + sizeof(uint32_t));

    memcpy(data, &uuid, sizeof(uint32_t));                                              // 将uuid保存到待加密缓冲区
    memcpy(data + sizeof(uint32_t), randomC, RANDOM_LENGTH);                            // 将randomC保存到待加密缓冲区
    memcpy(data + sizeof(uint32_t) + RANDOM_LENGTH, pub_key, strlen(pub_key));          // 将公钥保存到加密待加密缓冲区
    GM_SM4_encrypt(enc_data + sizeof(uint32_t), &enc_len, data, data_len, token, iv);   // 将加密缓冲区中的数据进行加密
    memcpy(enc_data, &enc_len, sizeof(uint32_t));                                       // 将data_size保存到待发送缓冲区
    send(fd, enc_data, enc_len + sizeof(uint32_t), 0);  // 将加密后的数据发送出去，返回数据格式： data_size(明文) | data(密文)

    printf("pub_key(mobile): %s\n", pub_key);
    printf("randomA : ");
    printHex(randomA, RANDOM_LENGTH);
    printf("randomB : ");
    printHex(randomB, RANDOM_LENGTH);
    printf("randomC : ");
    printHex(randomC, RANDOM_LENGTH);

    free(pub_key);
    pub_key = NULL;

    len = recv(fd, recv_buf, BUF_SIZE, 0);                                                  // 等待响应
    memcpy(&data_size, recv_buf, sizeof(uint32_t));                                         // 从响应数据中拿到data_size（响应数据的长度）
    dec_data = (uint8_t*)malloc(data_size + 16);
    GM_SM4_decrypt(dec_data, &dec_len, recv_buf + sizeof(uint32_t), data_size, token, iv);  // 解密响应数据中的密文

    memcpy(&resp_code, dec_data + sizeof(uint32_t), sizeof(uint32_t));                      // 从解密后的数据中拿到响应码
    if (resp_code == SUCCESS) {
        // 连接成功，保存连接交换得到的数据，并通过三个随机数生成对称密钥
        printf("初次连接成功！\n\n\n");
        KEY *key = (KEY*)malloc(sizeof(KEY));
        key->eckey_car = car_key;
        key->eckey_mobile = mobile_key;
        key->uuid = uuid;
        key->secret = generateSecret(randomA, randomB, randomC);
        setKEY(key);
    }

    free(dec_data);
    free(pin);
    dec_data = NULL;
    pin = NULL;
}

/**
 * 连接发起方二次连接函数（SM2加解密 + 数字签名）
 */
void secondConn(int fd) {
    struct timeval tv;
    gettimeofday(&tv, NULL);                                                    // 获取时间戳

    uint8_t *randomA, randomB[RANDOM_LENGTH], *randomC;
    uint8_t type = SECOND_CONNECT;                                              // 指定请求类型为二次连接
    uint32_t time = tv.tv_usec / 1000, data_size = 0;                           // 获得毫秒级时间戳 time
    EC_KEY *car_key = getEC_Key(uuid, 0x01);                                    // 根据uuid拿到车载端的密钥存储指针                            
    EC_KEY *mobile_key = getEC_Key(uuid, 0x00);                                 // 根据uuid拿到自己对应的密钥对指针
    uint8_t data[128];
    uint8_t *enc_data = NULL;
    uint32_t enc_len = 0, data_len = sizeof(uint32_t) * 2 + RANDOM_LENGTH;

    randomA = generate_secure_random_number(RANDOM_LENGTH);
    // 组装要传输的数据  time(时间戳) | randomA
    memcpy(data + sizeof(uint32_t), &time, sizeof(uint32_t));                   // 将时间戳存到加密缓冲区
    memcpy(data + sizeof(uint32_t) * 2, randomA, RANDOM_LENGTH);                //  randomA存到加密缓冲区
    encryptAndSign(&enc_data, &enc_len, data, data_len, car_key, mobile_key, uuid); // 调用非对称加密函数加密缓冲区的数据并签名
    // 按照数据传输协议组装数据  uuid | type | data_size | data
    memcpy(send_buf, &uuid, sizeof(uint32_t));                                  // uuid存到发送缓冲区
    memcpy(send_buf + sizeof(uint32_t), &type, sizeof(uint8_t));                // 请求类型type存到发送缓冲区
    memcpy(send_buf + sizeof(uint32_t) + sizeof(uint8_t), &enc_len, sizeof(uint32_t));  // data_size存到发送缓冲区
    memcpy(send_buf + sizeof(uint32_t)*2 + sizeof(uint8_t), enc_data, enc_len); // 加密数据存到发送缓冲区
    send(fd, send_buf, enc_len + 9, 0);                                         // 发送缓冲区中的数据
    free(enc_data);

    int len = recv(fd, recv_buf, BUF_SIZE, 0);   // 返回数据的格式为： data_size | data
    memcpy(&data_size, recv_buf, sizeof(uint32_t));  // 获取返回数据的长度data_size
    verifyAndDecrypt(&enc_data, &enc_len, recv_buf + sizeof(uint32_t), data_size, mobile_key, car_key, uuid);   // 验证签名并解密数据
    memcpy(randomB, enc_data + sizeof(uint32_t)*2, RANDOM_LENGTH);              // 从解密后的数据中拿到randomB

    randomC = generate_secure_random_number(RANDOM_LENGTH);
    memcpy(data + sizeof(uint32_t), randomC, RANDOM_LENGTH);                    // randomC存到发送缓冲区
    encryptAndSign(&enc_data, &enc_len, data, sizeof(uint32_t) + RANDOM_LENGTH, car_key, mobile_key, uuid); // 加密加密缓冲区的数据并签名
    memcpy(send_buf, &enc_len, sizeof(uint32_t));                               // 密文+签名长度data_size存到发送缓冲区
    memcpy(send_buf + sizeof(uint32_t), enc_data, enc_len);                     // 密文存到发送缓冲区
    send(fd, send_buf, sizeof(uint32_t) + enc_len, 0);    // data_size | data 发送数据

    printf("randomA: ");
    printHex(randomA, RANDOM_LENGTH);
    printf("randomB: ");
    printHex(randomB, RANDOM_LENGTH);
    printf("randomC: ");
    printHex(randomC, RANDOM_LENGTH);

    len = recv(fd, recv_buf, BUF_SIZE, 0);                                      // 等待并接收车载端数据返回
    memcpy(&data_size, recv_buf, sizeof(uint32_t));                             // 获取本次要接收的数据长度
    int ret = verifyAndDecrypt(&enc_data, &enc_len, recv_buf + sizeof(uint32_t), data_size, mobile_key, car_key, uuid); // 验签并解密
    memcpy(&len, enc_data + sizeof(uint32_t), sizeof(uint32_t));                // 从解密后的明文中拿到响应码
    if (len == SUCCESS) {
        printf("连接成功，二次连接请求结束。\n\n\n");
        setSecretAndUpdateTime(uuid, generateSecret(randomA, randomB, randomC));    // 保存本次协商生成的密钥，并更新时间戳
    } else {
        printf("连接失败，二次连接请求结束。\n\n\n");
    }
    free(randomA);
    free(randomC);
}

/**
 * 连接发起方数据通信函数
 */
void sendData(int fd) {
    struct timeval tv;
    gettimeofday(&tv, NULL);                                            // 获取当前时间

    uint32_t time = tv.tv_usec / 1000;                                  // 获取当前的毫米级时间戳
    uint8_t type = DATA_COMMUNICATION;                                  // 指定本次请求类型为数据通信
    uint8_t data[256];
    uint8_t *secret = getKey(uuid)->secret;                             // 根据uuid拿到连接过程协商出来的对称密钥

    EC_KEY *car_key = getEC_Key(uuid, 0x01);                            // 根据uuid拿到车载端的公钥
    EC_KEY *mobile_key = getEC_Key(uuid, 0x00);                         // 根据uuid拿到自己的密钥对
   
    uint8_t *enc_data = NULL;
    uint32_t enc_len = 0, data_size = 0, ret;
    memcpy(data + sizeof(uint32_t), &time, sizeof(uint32_t));           // 将时间戳time存到加密缓冲区
    strcpy(data + sizeof(uint32_t)*2, "hello world!");                  // 将要发送的消息体存到加密缓冲区
    encryptAndSign_SM4(&enc_data, &enc_len, data, 8 + strlen(data + 8), secret, mobile_key, uuid);  // 使用SM4加密数据并使用SM2签名
    // 按照数据传输协议组装数据
    memcpy(send_buf, &uuid, sizeof(uint32_t));                          // 携带上uuid明文到发送缓冲区，便于车载端识别发送者是谁
    memcpy(send_buf + sizeof(uint32_t), &type, sizeof(uint8_t));        // 请求类型type存到发送缓冲区
    memcpy(send_buf + sizeof(uint8_t) + sizeof(uint32_t), &enc_len, sizeof(uint32_t));  // 密文+签名的长度data_size存到发送缓冲区
    memcpy(send_buf + sizeof(uint8_t) + sizeof(uint32_t)*2, enc_data, enc_len);         // 将密文和签名存到发送缓冲区
    send(fd, send_buf, enc_len + 9, 0);                                 // 发送数据到车载端
    free(enc_data);

    int len = recv(fd, recv_buf, BUF_SIZE, 0);                          // 等待并接收车载端返回数据
    memcpy(&data_size, recv_buf, sizeof(uint32_t));                     // 拿到本轮要接收的数据长度
    verifyAndDecrypt_SM4(&enc_data, &enc_len, recv_buf + sizeof(uint32_t), data_size, secret, car_key, uuid);   // SM2验签，SM4解密
    memcpy(&ret, enc_data + sizeof(uint32_t), sizeof(uint32_t));        // 从解密后数据中得到响应码
    int data_len = enc_len - sizeof(uint32_t) * 2;
    memcpy(data, enc_data + sizeof(uint32_t)*2, data_len);              // 拿到解密后的消息体
    data[data_len] = 0;
    printf("收到响应，响应码：%d，响应数据： %s\n", ret, data);
}


int connectBluetooth() {
	struct sockaddr_in server;
	socklen_t saddrlen = sizeof(server);
 
	memset(&server, 0, sizeof(server));
	
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0)
	{
		perror("socket error");
        exit(1);
	}
 
	server.sin_family = AF_INET;
	server.sin_port = htons(8080);
	server.sin_addr.s_addr = inet_addr("127.0.0.1");
 
	if (connect(fd, (struct sockaddr *)&server, saddrlen) < 0)
	{
		perror("connect failed");
		exit(1);
	}
    return fd;
}


int main()
{
    send_buf = (uint8_t*)malloc(BUF_SIZE);
    recv_buf = (uint8_t*)malloc(BUF_SIZE);

    int IS_FIRST_CONN = 1;   // 标志是否连接成功过

    int fd = connectBluetooth();

    if (IS_FIRST_CONN) {    // 若是第一次连接，走初次连接流程
        printf("初次连接请求发起....\n");
        firstConn(fd);
    } else {                // 若不是第一次连接，走二次连接流程
        printf("二次连接请求发起....\n");
        secondConn(fd);
    }

    printf("发送数据....\n");
    sendData(fd);

    sleep(1);   // 睡眠1s等待发送缓存区数据全部发送完毕
    close(fd);
    free(send_buf);
    free(recv_buf);
    send_buf = NULL;
    recv_buf = NULL;

    return 0;
}